#include <stdio.h>
#include <string.h>
#include <poll.h>
#include <ifaddrs.h>
#include <linux/if_arp.h>
#include <netdb.h>

struct arphdr_ {
    unsigned short int ar_hrd;
    unsigned short int ar_pro;
    unsigned char ar_hln;
    unsigned char ar_pln;
    unsigned short int ar_op;
    unsigned char __ar_sha[ETH_ALEN];
    unsigned char __ar_sip[4];
    unsigned char __ar_tha[ETH_ALEN];
    unsigned char __ar_tip[4];
};

int main(int argc, char* argv[]) {
    int sock;
    char* name = 0;
    in_addr_t ip, sip;
    struct ifaddrs *ifaddr, *ifa;
    struct sockaddr_ll *addr = 0;
    struct sockaddr_in *saddr = 0;
    struct arphdr_ arp_resp;
    
    if (argc < 2) {
        fprintf(stderr, "Usage: %s IP\n", argv[0]);
        return 1;
    }
    
    if (!inet_aton(argv[1], &ip)) {
        perror("inet_aton");
        return 1;
    }
    
    getifaddrs(&ifaddr);
    for (ifa = ifaddr; ifa; ifa = ifa->ifa_next) {
        if (ifa->ifa_addr->sa_family == AF_INET) {
            in_addr_t if_ip = ((struct sockaddr_in*)ifa->ifa_addr)->sin_addr.s_addr;
            in_addr_t if_mask = ((struct sockaddr_in*)ifa->ifa_netmask)->sin_addr.s_addr;
            if ((if_ip & if_mask) == (ip & if_mask)) {
                name = ifa->ifa_name;
                saddr = (struct sockaddr_in*)ifa->ifa_addr;
                break;
            }
        }
    }
    if (name)
    for (ifa = ifaddr; ifa; ifa = ifa->ifa_next) {
        if (!strcmp(name, ifa->ifa_name) && ifa->ifa_addr->sa_family == AF_PACKET) {
            addr = (struct sockaddr_ll*)ifa->ifa_addr;
            addr->sll_protocol = htons(ETH_P_ARP);
            break;
        }
    }
    
    if (!addr) {
        fprintf(stderr, "I'm sorry, Dave. I'm afraid I can't find appropriate interface.\n");
        return 1;
    }
    
    if ((sock = socket(AF_PACKET, SOCK_DGRAM, ETH_P_ARP)) < 0) {
        perror("socket");
        return 1;
    }
    
    if (bind(sock, (struct sockaddr*)addr, sizeof(struct sockaddr_ll)) < 0) {
        perror("bind");
        return 1;
    }
    
    struct sockaddr_ll mac_broadcast;
    mac_broadcast.sll_family = AF_PACKET;
    mac_broadcast.sll_protocol = htons(ETH_P_ARP);
    mac_broadcast.sll_ifindex = addr->sll_ifindex;
    mac_broadcast.sll_hatype = ARPHRD_ETHER;
    mac_broadcast.sll_pkttype = PACKET_BROADCAST;
    mac_broadcast.sll_halen = ETH_ALEN;
    memset(mac_broadcast.sll_addr, 0xFF, ETH_ALEN);
    
    struct arphdr_ arp_req;
    arp_req.ar_hrd = htons(0x0001);
    arp_req.ar_pro = htons(0x0800);
    arp_req.ar_hln  = ETH_ALEN;
    arp_req.ar_pln  = 4;
    arp_req.ar_op = htons(1);
    memcpy(arp_req.__ar_sha, addr->sll_addr, ETH_ALEN);
    memcpy(arp_req.__ar_sip, &saddr->sin_addr, 4);
    memset(arp_req.__ar_tha, 0, ETH_ALEN);
    memcpy(arp_req.__ar_tip, &ip, 4);
    
    if (sendto(sock, &arp_req, sizeof(struct arphdr_), 0, (struct sockaddr*)&mac_broadcast, sizeof(struct sockaddr_ll)) < 0) {
        perror("sendto");
        return 1;
    }
    
    struct pollfd pfd;
    pfd.fd = sock;
    pfd.events = POLLIN;
    if (poll(&pfd, 1, 5000) <= 0) {
        fprintf(stderr, "I'm sorry, Dave. I'm afraid I ran out of time.\n");
        return 1;
    }
    if (recv(sock, &arp_resp, sizeof(struct arphdr_), 0) < 0) {
        perror("recv");
        return 1;
    }
    if (arp_resp.ar_op == ntohs(2) && *((in_addr_t*)&arp_resp.__ar_sip) == ip) {
        printf("%02X:%02X:%02X:%02X:%02X:%02X\n", arp_resp.__ar_sha[0], arp_resp.__ar_sha[1],
            arp_resp.__ar_sha[2], arp_resp.__ar_sha[3], arp_resp.__ar_sha[4], arp_resp.__ar_sha[5]);
    } else {
        fprintf(stderr, "I'm sorry, Dave. I'm afraid I recieved response from a wrong host.\n");
    }
    
    close(sock);
    freeifaddrs(ifaddr);
    return 0;
}
